package org.noote.shaky.pad;

import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.os.PowerManager;

import org.noote.libs.bluetooth.DeviceList_Activity;
import org.noote.libs.bluetooth.SPPClient;
import org.noote.libs.robot.Robot_Animation;
import org.noote.libs.robot.object3d.Robot_Skeleton3D;
import org.noote.libs.shaky.comm.BluetoothCommandEvents;
import org.noote.libs.shaky.comm.BluetoothCommandProtocol;
import org.noote.libs.shaky.configuration.RobotSettings;
import org.noote.libs.shaky.configuration.RobotStates;
import org.noote.libs.sogle.Light;
import org.noote.libs.sogle.Quaternion;
import org.noote.libs.sogle.Vector3D;
import org.noote.libs.widget.WidgetEvent_OnChangeItem;
import org.noote.libs.widget.WidgetEvent_OnClick;
import org.noote.libs.widget.WidgetEvent_OnTouch;
import org.noote.libs.widget.WidgetRenderer;
import org.noote.libs.widget.WidgetTheme;
import org.noote.libs.widget.Widget_DirectionPad;
import org.noote.libs.widget.Widget_Label;
import org.noote.libs.widget.Widget_LabelPad;
import org.noote.libs.widget.Widget_Pad;
import org.noote.libs.widget.WidgetTexturePack;
import org.noote.libs.widget.Widget_Window;
import org.noote.libs.widget.Widget_ZoomPad;
import org.noote.libs.widget.Widget_Carousel;
import org.noote.libs.widget.Widget_CarouselItem;
import org.noote.libs.sogle.helper.LineMesh_Grid;
import org.noote.libs.sogle.helper.FPSCounter;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent;
import android.widget.Toast;

public class Shaky_Activity extends Activity {

	static final String TAG = "Shaky_Activity";
	//
	public static final String PREFS_NAME = "Shaky_PAD";
	public static final String PREFS_NAME_BTADDRESS = "BT Address";
	
	private static final int GETTING_DEVICE = 0;
	private static final int REQUEST_ENABLE_BT = 1;

	private String _sAddressDevice = "";
	SPPClient _SPPClient = null;
	protected int _iReadyStep;
	boolean _bTransmitData = false;
	
	private RobotSettings _settings = new RobotSettings();
	private RobotStates _states = new RobotStates();
	
	private Shaky_Skeleton _bot;
	FPSCounter _botFrameCounter = new FPSCounter();
	
	PowerManager.WakeLock wl = null;

	private ShakyGLSurfaceView mGLView;
    private ShakyRenderer mRenderer;
    
    Widget_LabelPad _transmit_pad, _connection_pad, _setting_pad;
    Widget_DirectionPad _dir_pad;
    Widget_ZoomPad _zoom_pad;
    Widget_Carousel _animation_carousel;
    Widget_Label _label_message, _label_robotFPS, _label_guiFPS;
    Widget_Pad _draw_mode_pad;
    
    boolean _move_front = false, _move_back = false;
    boolean _move_up = false, _move_down = false;
    boolean _rotate_left = false, _rotate_right = false;
    
    float fUpCameraFactor = 0.5f, fUpCameraLootAtMin=3, fUpCameraLookAtMax=40, fUpCameraMin=1, fUpCameraMax=45;
    float fRotCamera = 0;
    float fZoomCameraFactor = 0.75f, fZoomCameraMin=20, fZoomCameraMax=60;
    
    int _iAnimationChoosen=0;
    int _iDrawMode=0;
		
    class ShakyRenderer extends WidgetRenderer
    {
    	static final int TEX_SHOW_WIRED=0, TEX_SHOW_BOXES=1, TEX_SHOW_MESHES=2;
    	WidgetTexturePack _myTextures = new WidgetTexturePack();
    	WidgetTexturePack _myAnimationIcons = new WidgetTexturePack();
    	Light _light=null;
    	
        public ShakyRenderer(Context context)
        {
        	super(context);
        }
    	
        @Override
        public void onCreateScene(GL10 gl)
    	{
    		super.onCreateScene(gl);
    		
    		if(_myTextures.create(gl, 3))
    		{
	    		_myTextures.loadImageFromResource(_context, R.drawable.robot_wired, gl, TEX_SHOW_WIRED);
	    		_myTextures.loadImageFromResource(_context, R.drawable.robot_boxes, gl, TEX_SHOW_BOXES);
	    		_myTextures.loadImageFromResource(_context, R.drawable.robot_wired, gl, TEX_SHOW_MESHES);
    		}
    		
            lighting(false);
            
            setBackGroundColor(0.5f, 0.5f, 0.5f);

            _camera.setLookAt(new Vector3D(0, 20, 50), new Vector3D(0, 20, 0), 0);
            
            LineMesh_Grid floor = new LineMesh_Grid(50.0f, 50.0f, 20, 20);
            //Mesh_Plane floor = new Mesh_Plane(50.0f, 50.0f, 10, 10);
            _listObjects.add(floor);
            
            _bot = new Shaky_Skeleton();
            _bot.create(_context);
            _bot.setPosition(0,30,0);
            _listObjects.add(_bot);
            
            _light = new Light();
            _light.enable(true);
            _listLights.add(_light);
            
            _label_message = _dekstop.createLabel();
            _label_message.setWorkSpace(getWidth()/2, 32, 256, 32);
            _label_message.setTextColor(0, 1, 1);
            _dekstop.add(_label_message);
            
            // a carroussel with all animation loops
            int iAnimCount=0;
            _myAnimationIcons.create(gl, _bot._anims.size());
            _animation_carousel = _dekstop.createCarousel();
            _animation_carousel.setRightDocked();
            for(Robot_Animation a : _bot._anims)
            {
            	// TODO
            	_myAnimationIcons.loadImageFromResource(_context, R.drawable.no_anim, gl, iAnimCount);
            	
            	Widget_CarouselItem item = _animation_carousel.createCarouselItem();
            	item.setText(a.getName());
            	item.setLongData(iAnimCount);
            	item.setIcon(_myAnimationIcons.getTextureID(iAnimCount));
            	_animation_carousel.add(item);
            	
            	iAnimCount++;
            }
            _animation_carousel.setOnChangeItem(new WidgetEvent_OnChangeItem()
            {
				@Override
				public boolean onSelectedItem(Widget_Window window, Widget_Window item) {
	    			_iAnimationChoosen = (int)item.getLongData();
					return true;
				}
            });
            _animation_carousel.selectItem(0);
            _dekstop.add(_animation_carousel);

            // directional pad :)
            _dir_pad = _dekstop.createDirectionPad();
            _dir_pad.setPosition(_dir_pad.getWidth()/2, getHeight()-(int)(_dir_pad.getHeight()*2.5f));
            _dir_pad.setOnTouch(new WidgetEvent_OnTouch()
            {
				@Override
				public boolean onPressed(Widget_Window window) {

					if(_dir_pad.getButtonUp())
    	    		{
    	    			_move_up = true;
    	    			_move_down = false;
    	    		}
    	    		else if(_dir_pad.getButtonDown())
    	    		{
    	    			_move_up = false;
    	    			_move_down = true;
    	    		}
    	    		if(_dir_pad.getButtonRight())
    	    		{
    	    			_rotate_right = true;
    	    			_rotate_left = false;
    	    		}
    	    		else if(_dir_pad.getButtonLeft())
    	    		{
    	    			_rotate_right = false;
    	    			_rotate_left = true;
    	    		}
    	    		
					return true;
				}

				@Override
				public boolean onMoved(Widget_Window window) {
					return true;
				}

				@Override
				public boolean onReleased(Widget_Window window) {
					_move_up = _move_down = _rotate_right = _rotate_left = false;
					return true;
				}
            });
            _dekstop.add(_dir_pad); 	

            // zoom pad :)
            _zoom_pad = _dekstop.createZoomPad();
            _zoom_pad.setPosition(_zoom_pad.getWidth()/2, (int)(_zoom_pad.getHeight()*1.5f));
            _zoom_pad.setOnTouch(new WidgetEvent_OnTouch()
            {
				@Override
				public boolean onPressed(Widget_Window window) {

	    			if(_zoom_pad.getButtonFront())
	    			{
	    				_move_front = true;
	    				_move_back = false;
	    			}
	    			else if(_zoom_pad.getButtonBack())
	    			{
	    				_move_front = false;
	    				_move_back = true;	    	    				
	    			}
    	    		
					return true;
				}

				@Override
				public boolean onMoved(Widget_Window window) {
					return true;
				}

				@Override
				public boolean onReleased(Widget_Window window) {
					_move_front = _move_back = false;
					return true;
				}
            });
            _dekstop.add(_zoom_pad);

            // settings pad :)
            _setting_pad = _dekstop.createLabelPad();
            _setting_pad.setPosition(0, getHeight()-_setting_pad.getHeight());
            _setting_pad.setText("Setting");
            _setting_pad.setSystemIcon(WidgetTheme.ICON_SETTING);
            _setting_pad.setOnClick(new WidgetEvent_OnClick()
            {
				@Override
				public boolean onClick(Widget_Window window) {
					Intent intent = new Intent(_context,
	    					Setting_Activity.class);
	    			startActivity(intent);
					return true;
				}
            });
            _dekstop.add(_setting_pad);

            // connection pad :)
            _connection_pad = _dekstop.createLabelPad();
            _connection_pad.setPosition(_setting_pad.getPositionX()+_setting_pad.getWidth(), getHeight()-_connection_pad.getHeight());
            _connection_pad.setText("Connection");
            _connection_pad.setOff();
            _connection_pad.setOnClick(new WidgetEvent_OnClick()
            {
				@Override
				public boolean onClick(Widget_Window window) {
	    			if(_connection_pad.isOn())
	    				btConnect();
    	    		else
    	    			btDisconnect();
					return true;
				}
            });
            _dekstop.add(_connection_pad);
          
            // transmit pad :)
            _transmit_pad = _dekstop.createLabelPad();
            _transmit_pad.setPosition(_connection_pad.getPositionX()+_connection_pad.getWidth(), getHeight()-_transmit_pad.getHeight());
            _transmit_pad.setText("Transmition");
            _transmit_pad.setOff();
            _transmit_pad.enableWindow(false);
            _transmit_pad.setOnClick(new WidgetEvent_OnClick()
            {
				@Override
				public boolean onClick(Widget_Window window) {
    	    		if(_transmit_pad.isOn())
    	    			_bTransmitData = true;
    	    		else
    	    			_bTransmitData = false;
					return true;
				}
            });
            _dekstop.add(_transmit_pad);
            
            _label_robotFPS = _dekstop.createLabel();
            _label_robotFPS.setWorkSpace(_setting_pad.getPositionX()+16, _setting_pad.getPositionY()-16, 128, 32);
            _label_robotFPS.setTextColor(_dekstop.getTheme().colorLightGray);
            _dekstop.add(_label_robotFPS);
            
            // draw options
            _draw_mode_pad = _dekstop.createPad();
            _draw_mode_pad.setPosition(0, 0);
            _draw_mode_pad.setText("Wired");
            _draw_mode_pad.setIcon(_myTextures.getTextureID(TEX_SHOW_WIRED));
            _draw_mode_pad.setOnClick(new WidgetEvent_OnClick()
            {
				@Override
				public boolean onClick(Widget_Window window) {
	    			setDrawMode(_iDrawMode+1);
					return true;
				}
            });
            _dekstop.add(_draw_mode_pad);
            
            _label_guiFPS = _dekstop.createLabel();
            _label_guiFPS.setTextColor(_dekstop.getTheme().colorLightGray);
            _label_guiFPS.setWorkSpace(_draw_mode_pad.getPositionX()+16, _draw_mode_pad.getPositionY()+_draw_mode_pad.getHeight()+16, 128, 32);
            _dekstop.add(_label_guiFPS);

            calculCameraPosition();
            
			setDrawMode(2);
    	}
    	
        @Override
        public void onFrame(float fTimeSlice) {
 
        	// Camera stuff
    		if(_move_front)
    			fZoomCameraFactor -= 0.25*fTimeSlice;
    		else if(_move_back)
    			fZoomCameraFactor += 0.25*fTimeSlice;
    		fZoomCameraFactor = fZoomCameraFactor>1.0f?1.0f:fZoomCameraFactor<0?0:fZoomCameraFactor;
    		if(_move_up)
    			fUpCameraFactor += 0.25*fTimeSlice;
    		else if(_move_down)
    			fUpCameraFactor -= 0.25*fTimeSlice;
    		fUpCameraFactor = fUpCameraFactor>1.0f?1.0f:fUpCameraFactor<0?0:fUpCameraFactor;
    		if(_rotate_left)
    			fRotCamera += 35.0*fTimeSlice;
    		else if(_rotate_right)
    			fRotCamera -= 35.0*fTimeSlice;
    		fRotCamera = fRotCamera>145.0f?145.0f:fRotCamera<-145?-145:fRotCamera;
    		
    		calculCameraPosition();
    		
    		_label_guiFPS.setText(String.format("App FPS : %2.1f", _fps.getFPS()));
    		_label_robotFPS.setText(String.format("Shaky FPS : %2.1f (%d frames in %3.2f seconds)", _botFrameCounter.getFPS(), _botFrameCounter.getGlobalFrameCount(), _botFrameCounter.getGlobalTimeCount()));
        	
    		// Robot anim
    		if(_bot.isAnimationFinish())
    			_bot.setAnimation(_iAnimationChoosen);
    		_bot.doAnimation(fTimeSlice);
        	_bot.update();
    	}

        void setDrawMode(int iMode)
        {
        	_iDrawMode = iMode%4;
        	switch(_iDrawMode)
        	{
        	case 0:
    			_bot.drawMode(Robot_Skeleton3D._DRAW_WIRED);
    			mRenderer.lighting(false);
    			_draw_mode_pad.setText("Wire");
    			_draw_mode_pad.setIcon(_myTextures.getTextureID(TEX_SHOW_WIRED));
        		break;
        	case 1:
    			_bot.drawMode(Robot_Skeleton3D._DRAW_BOX);
    			mRenderer.lighting(false);
    			_draw_mode_pad.setText("Box");
    			_draw_mode_pad.setIcon(_myTextures.getTextureID(TEX_SHOW_BOXES));
        		break;
        	case 2:
    			_bot.drawMode(Robot_Skeleton3D.DRAW_MESH);
    			mRenderer.lighting(true);
    			_draw_mode_pad.setText("Mesh");
    			_draw_mode_pad.setIcon(_myTextures.getTextureID(TEX_SHOW_MESHES));
        		break;
        	case 3:
    			_bot.drawMode(Robot_Skeleton3D._DRAW_WIRED + Robot_Skeleton3D.DRAW_MESH);
    			mRenderer.lighting(false);
    			_draw_mode_pad.setText("Mesh+Wire");
    			_draw_mode_pad.setIcon(_myTextures.getTextureID(TEX_SHOW_MESHES));
        		break;
        	}
        }
        
        void calculCameraPosition()
        {
        	Vector3D vLookAt=new Vector3D(), vPosition=new Vector3D();
        	
    		Quaternion qCamera = Quaternion.fromEuler(0, fRotCamera, 0);
    		vLookAt.set(0f, fUpCameraLootAtMin+fUpCameraLookAtMax*fUpCameraFactor, 0f);
    		vPosition.set(0f, fUpCameraMin+fUpCameraMax*fUpCameraFactor, fZoomCameraMin+fZoomCameraMax*fZoomCameraFactor);
    		
    		// rotation
    		vPosition = Quaternion.mult(qCamera, vPosition);
    		
    		mRenderer._camera.setLookAt(vPosition, vLookAt, 0f);
    		
    		//_light.setPosition(vPosition.x, vPosition.y, vPosition.z);
    		_light.setDirection(vPosition.x, vPosition.y+2.0f, vPosition.z);
        }
    }
	
	class ShakyGLSurfaceView extends GLSurfaceView {
		
	    public ShakyGLSurfaceView(Context context) {
	        super(context);
	        
	        mRenderer = new ShakyRenderer(context);
	        setRenderer(mRenderer);
	        setPreserveEGLContextOnPause(true);
	    }

	    @Override
		public boolean onTouchEvent(final MotionEvent event)
	    {
	        queueEvent(new Runnable(){
	            @Override
				public void run() {
	            	
	    	    	mRenderer.onTouchEvent(event);

	            }});
	        return true;
	    }
	}

	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        _SPPClient = new SPPClient(mBTEvents);
        
        mGLView = new ShakyGLSurfaceView(this);
        setContentView(mGLView);
   	}

	@Override
    public void onStart() {
        super.onStart();

        // Register for broadcasts when a device is disconnected !
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED);
        this.registerReceiver(mReceiver, filter);
        
        final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mBluetoothAdapter != null && !mBluetoothAdapter.isEnabled()) {
		    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
		    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
		    return;
		}
	}
	@Override
	public void onStop()
	{
		this.unregisterReceiver(mReceiver);
		
		super.onStop();
	}

    @Override
    protected void onPause() {
        super.onPause();
        
        mGLView.onPause();
        
		if(wl != null ) wl.release();        
    }

    @Override
    protected void onResume() {
        super.onResume();
        mGLView.onResume();
        
		if(wl == null)
		{
			PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
		    wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "Remote Robot");
		}
		if(wl != null ) wl.acquire();
    }
    
	@Override
	public void onBackPressed()
    {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setTitle(getString(R.string.dlg_exit_title));
		builder.setMessage(getString(R.string.dlg_exit_text));
		builder.setPositiveButton(getString(R.string.dlg_button_exit),
				new DialogInterface.OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int id) {
						btDisconnect();
						finish();
					}
				});
		builder.setNegativeButton(getString(R.string.dlg_button_cancel),
				new DialogInterface.OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int id) {
						dialog.cancel();
					}
				});
		AlertDialog alert = builder.create();
		alert.show();
    }

    
    boolean btHaveAddress()
    {
		SharedPreferences settings = getSharedPreferences(Shaky_Activity.PREFS_NAME, 0);
        _sAddressDevice = settings.getString(Shaky_Activity.PREFS_NAME_BTADDRESS, "");
        if(_sAddressDevice.length() == 0)
        {
           Intent intent = new Intent(getApplicationContext(), DeviceList_Activity.class);
           startActivityForResult(intent, GETTING_DEVICE);
           return false;
        }
        
        return true;
    }
    
    void btConnect()
    {
		_connection_pad.setOff();
		_transmit_pad.setOff();
		_transmit_pad.enableWindow(false);
		pushMessage("Try BT connection...");
		if(!btHaveAddress()) return;
		if(!_SPPClient.isConnected())
		{
			if(!_SPPClient.connect(_sAddressDevice))
			{	
				pushMessage("Connection to BT:"+_sAddressDevice+" impossible !!!");
			}
		}
    }
    void btDisconnect()
    {
		pushMessage("Try BT disconnection...");
		if(_SPPClient.isConnected())
		{
			//if(!_SPPClient.disconnect())
			{
				pushMessage("Disconnection from BT:"+_sAddressDevice+" impossible !!!");
			}
		}
		_connection_pad.setOff();
		_transmit_pad.setOff();
		_transmit_pad.enableWindow(false);
    }

    void pushMessage(String sString)
    {
    	// TODO Add a list message widget :)
    	_label_message.setText(sString);
    }
    
	@Override
	protected void onActivityResult (int requestCode, int resultCode, Intent data)
	{
		switch(requestCode)
		{
		case REQUEST_ENABLE_BT:
			if(resultCode == RESULT_OK)
			{
				btHaveAddress();
			}
			break;
		case GETTING_DEVICE:
			if(resultCode == RESULT_OK)
			{
				String sAddressDevice = data.getStringExtra(DeviceList_Activity.EXTRA_DEVICE_ADDRESS);
				
				SharedPreferences settings = getSharedPreferences(Shaky_Activity.PREFS_NAME, 0);
				SharedPreferences.Editor settings_editor = settings.edit(); 
				settings_editor.putString(Shaky_Activity.PREFS_NAME_BTADDRESS, sAddressDevice);
				settings_editor.commit();
			}
			break;
		}
	}

    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            // When discovery finds a device
            if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
				_states._bBluetoothOnline = false;
				pushMessage("Disconnected...");            	
            }
        }
    };

    final BluetoothCommandEvents mBTEvents = new BluetoothCommandEvents()
	{
		@Override
		public void onConnected()
		{
			Toast.makeText(getApplicationContext(), "Connected",
					Toast.LENGTH_SHORT).show();
			_states._bBluetoothOnline = true;
			pushMessage("Online...");
			_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskConnection()); // ask connect
		}
		@Override
		public void onDisconnected()
		{
        	Toast.makeText(getApplicationContext(), "Disconnected", Toast.LENGTH_SHORT).show();
        	_states._bBluetoothOnline = false;
        	_states._bReady = false;
        	
			_connection_pad.setOff();
			_transmit_pad.setOff();
			_transmit_pad.enableWindow(false);
		}
		@Override
		public void onError(String sError)
		{
        	Toast.makeText(getApplicationContext(), "Get Error : " + sError, Toast.LENGTH_LONG).show();
       	}
		@Override
		public void onText(String sText)
		{
        	Toast.makeText(getApplicationContext(), "Get text : " + sText, Toast.LENGTH_LONG).show();
		}
		@Override
		public void onReady(String sStatus) {
			_iReadyStep |= 0x01;
			
			_SPPClient.sendMessage(BluetoothCommandProtocol.sendAskVersion()); // ask version
			
			isReady();
		}
		@Override
		public void onVersion(String robotname, String robotversion)
		{
			_iReadyStep |= 0x2;
			
			_settings._sRobot_Name = robotname;
			_settings._sRobot_Version = robotversion;
			
			isReady();
		}
		@Override
		public void onTick(float fTimeSlice) {
			if(_states._bReady && _bTransmitData)
			{
				//_botFrameCounter.update(fTimeSlice);
				_botFrameCounter.tick();
				_SPPClient.sendMessage(_bot.buildTransmitMessage());
			}
		}
		private void isReady()
		{
    		if(_states._bReady == false && _iReadyStep == (0x01|0x02))
    		{
    			_states._bReady = true;
    			pushMessage("Ready !");
    			 _transmit_pad.enableWindow(true);
    			 _connection_pad.setOn();
    		}
    		else if(_states._bReady == false)
    		{
    			String sWait = "Wait ";
    			sWait += ((_iReadyStep & 0x01) == 0x01)?"*":".";
    			sWait += ((_iReadyStep & 0x02) == 0x02)?"*":".";
    			pushMessage(sWait);
    			_connection_pad.setOff();
    			_transmit_pad.setOff();
    			_transmit_pad.enableWindow(false);
    		}
		}
	};
}